--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
meshchatx/src/backend/integrity_manager.py ec74a2651e897325b14269ae52d11b1e12f0b6e2 (ec74a265) Text, 13.33 KB
T8b949e# SPDX-License-Identifier: 0BSD
Tff7b72import T7ee787fnmatch
Tff7b72import T7ee787hashlib
Tff7b72import T7ee787json
Tff7b72import T7ee787math
Tff7b72import T7ee787os
Tff7b72import T7ee787sqlite3
Tff7b72from T7ee787datetime Tff7b72import Te6edf3UTCTb4b4b4, Te6edf3datetime
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
Tff7b72from T7ee787typing Tff7b72import Te6edf3ClassVar
Tff7b72class T56d364CriticalIntegrityErrorTb4b4b4(Tf85149RuntimeErrorTb4b4b4)Tb4b4b4:
T8b949e"""Raised when tampering is detected in identity or database files at startup."""
Tff7b72def Td2a8ffselect_critical_integrity_issuesTb4b4b4(Te6edf3issuesTb4b4b4: Tffa657listTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4:
Tff7b72return Tb4b4b4[
Te6edf3issue
Tff7b72for Te6edf3issue Tff7b72in Te6edf3issues
Tff7b72if Tffa657anyTb4b4b4(Te6edf3marker Tff7b72in Te6edf3issue Tff7b72for Te6edf3marker Tff7b72in Te6edf3IntegrityManagerTff7b72.Td2a8ffCRITICAL_ISSUE_MARKERSTb4b4b4)
Tb4b4b4]
Tff7b72class T56d364IntegrityManagerTb4b4b4:
T8b949e"""Manages the integrity of the database and identity files at rest."""
Te6edf3CRITICAL_ISSUE_MARKERSTb4b4b4: Te6edf3ClassVarTb4b4b4[Tffa657tupleTb4b4b4[Tffa657strTb4b4b4, Tff7b72.Tff7b72.Tff7b72.Tb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffCritical security component integrity compromisedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffDatabase structural issueTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffIdentity mismatchTa5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# Filename globs frequently rewritten by RNS/LXMF or SQLite that should be
T8b949e# ignored during integrity checks.
Te6edf3IGNORED_PATTERNSTb4b4b4: Te6edf3ClassVarTb4b4b4[Tffa657listTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6ff*-walTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*-shmTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*-journalTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*.tmpTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*.lockTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*.logTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*~Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff*.ratchetsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff.DS_StoreTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffThumbs.dbTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffintegrity-manifest.jsonTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffoutbound_stamp_costsTa5d6ff"Tb4b4b4,
Tb4b4b4]
T8b949e# Any path containing one of these directory names is treated as volatile.
T8b949e# The whole lxmf_router tree (ratchets, message store, peer/announce
T8b949e# tables, delivery state) is rewritten continuously during normal
T8b949e# operation and must not be monitored, or it produces a constant stream of
T8b949e# false integrity warnings.
Te6edf3VOLATILE_DIRSTb4b4b4: Te6edf3ClassVarTb4b4b4[Tffa657setTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6fflxmf_routerTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffratchetsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffmessagestoreTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftmpTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrecordingsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffgreetingsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdocsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffbotsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffringtonesTa5d6ff"Tb4b4b4,
Tb4b4b4}
T8b949e# Exact RNS/LXMF state filenames that change on their own during runtime.
Te6edf3VOLATILE_FILENAMESTb4b4b4: Te6edf3ClassVarTb4b4b4[Tffa657setTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffoutbound_stamp_costsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffnode_statsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffavailable_ticketsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflocal_deliveriesTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflocally_processedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflocally_deliveredTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpeersTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffannounce_cacheTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_tableTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffannounce_tableTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffknown_destinationsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffheld_announcesTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftransport_identityTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffidentity_cacheTa5d6ff"Tb4b4b4,
Tb4b4b4}
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_dirTb4b4b4, Te6edf3database_pathTb4b4b4, Te6edf3identity_hashTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3storage_dirTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffdatabase_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3database_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffidentity_hash Tff7b72= Te6edf3identity_hash
Tff7b72selfTff7b72.Td2a8ffmanifest_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72/ Ta5d6ff"Ta5d6ffintegrity-manifest.jsonTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffissues Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72def Td2a8ff_should_ignoreTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3rel_pathTb4b4b4)Tb4b4b4:
T8b949e"""Determine if a file path is volatile RNS/LXMF state to skip.
Critical security components living directly under the identity storage
directory (identity, config, database.db) are never ignored;
only the continuously-rewritten LXMF router tree, ratchets, message
store and known RNS state files are excluded.
"""
Te6edf3path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3rel_pathTb4b4b4)
Te6edf3path_parts Tff7b72= Te6edf3pathTff7b72.Td2a8ffparts
Tff7b72if Tffa657anyTb4b4b4(Te6edf3part Tff7b72in Tff7b72selfTff7b72.Td2a8ffVOLATILE_DIRS Tff7b72for Te6edf3part Tff7b72in Te6edf3path_partsTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72True
Te6edf3filename Tff7b72= Te6edf3path_partsTb4b4b4[Tff7b72-T79c0ff1Tb4b4b4]
Tff7b72if Te6edf3filename Tff7b72in Tff7b72selfTff7b72.Td2a8ffVOLATILE_FILENAMESTb4b4b4:
Tff7b72return Tff7b72True
Tff7b72if Tffa657anyTb4b4b4(Te6edf3fnmatchTff7b72.Td2a8fffnmatchTb4b4b4(Te6edf3filenameTb4b4b4, Te6edf3patternTb4b4b4) Tff7b72for Te6edf3pattern Tff7b72in Tff7b72selfTff7b72.Td2a8ffIGNORED_PATTERNSTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3file_pathTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3file_pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3sha256_hash Tff7b72= Te6edf3hashlibTff7b72.Td2a8ffsha256Tb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Te6edf3file_pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Tff7b72for Te6edf3byte_block Tff7b72in Tffa657iterTb4b4b4(Tff7b72lambdaTb4b4b4: Te6edf3fTff7b72.Td2a8ffreadTb4b4b4(T79c0ff4096Tb4b4b4)Tb4b4b4, Ta5d6ffbTa5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3sha256_hashTff7b72.Td2a8ffupdateTb4b4b4(Te6edf3byte_blockTb4b4b4)
Tff7b72return Te6edf3sha256_hashTff7b72.Td2a8ffhexdigestTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ff_calculate_entropyTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3file_pathTb4b4b4)Tb4b4b4:
T8b949e"""Calculate Shannon entropy of a file to detect content type shifts."""
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3file_pathTb4b4b4)Tb4b4b4:
Tff7b72return T79c0ff0
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3file_pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
T8b949e# Sample up to 64KB for performance
Te6edf3data Tff7b72= Te6edf3fTff7b72.Td2a8ffreadTb4b4b4(T79c0ff65536Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3dataTb4b4b4:
Tff7b72return T79c0ff0
Te6edf3byte_counts Tff7b72= Tb4b4b4[T79c0ff0Tb4b4b4] Tff7b72* T79c0ff256
Tff7b72for Te6edf3b Tff7b72in Te6edf3dataTb4b4b4:
Te6edf3byte_countsTb4b4b4[Te6edf3bTb4b4b4] Tff7b72+Tff7b72= T79c0ff1
Te6edf3entropy Tff7b72= T79c0ff0
Te6edf3total Tff7b72= Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)
Tff7b72for Te6edf3count Tff7b72in Te6edf3byte_countsTb4b4b4:
Tff7b72if Te6edf3count Tff7b72> T79c0ff0Tb4b4b4:
Te6edf3p Tff7b72= Te6edf3count Tff7b72/ Te6edf3total
Te6edf3entropy Tff7b72-Tff7b72= Te6edf3p Tff7b72* Te6edf3mathTff7b72.Td2a8fflog2Tb4b4b4(Te6edf3pTb4b4b4)
Tff7b72return Te6edf3entropy
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return T79c0ff0
Tff7b72def Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3db_pathTb4b4b4)Tb4b4b4:
T8b949e"""Use SQLite's PRAGMA integrity_check to verify the database."""
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3db_pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72FalseTb4b4b4, Ta5d6ff"Ta5d6ffDatabase file does not existTa5d6ff"
Tff7b72tryTb4b4b4:
T8b949e# Use read-only mode for checking
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6fffile:Tffd700{Te6edf3db_pathTffd700}Ta5d6ff?mode=roTa5d6ff"Tb4b4b4, Te6edf3uriTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3cursor Tff7b72= Te6edf3connTff7b72.Td2a8ffcursorTb4b4b4(Tb4b4b4)
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA integrity_checkTa5d6ff"Tb4b4b4)
Te6edf3result Tff7b72= Te6edf3cursorTff7b72.Td2a8fffetchoneTb4b4b4(Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3result Tff7b72== Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4, Te6edf3result
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Tff7b72FalseTb4b4b4, Tffa657strTb4b4b4(Te6edf3eTb4b4b4)
Tff7b72def Td2a8ffcheck_integrityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3critical_onlyTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4)Tb4b4b4:
T8b949e"""Verify the current state against the last saved manifest using advanced analytics.
When critical_only is True, only identity and database markers are checked.
The full storage walk runs later so startup is not blocked by hashing every file.
"""
Tff7b72if Tff7b72not Tff7b72selfTff7b72.Td2a8ffmanifest_pathTff7b72.Td2a8ffexistsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72TrueTb4b4b4, Tb4b4b4[Ta5d6ff"Ta5d6ffInitial run - no manifest yetTa5d6ff"Tb4b4b4]
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3issues Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3manifest_files Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)
Te6edf3manifest_metadata Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)
Te6edf3m_id Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffidentityTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4)
T8b949e# Always check for identity mismatch first as it's a fundamental security issue
Tff7b72if Tff7b72selfTff7b72.Td2a8ffidentity_hash Tff7b72and Te6edf3m_id Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4, Tff7b72selfTff7b72.Td2a8ffidentity_hashTb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffIdentity mismatch! Manifest belongs to: Tffd700{Te6edf3m_idTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# Check Database (Math-based structural check + Entropy stability + Hash)
Tff7b72if Tff7b72selfTff7b72.Td2a8ffdatabase_pathTff7b72.Td2a8ffexistsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdatabase_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdatabase_pathTff7b72.Td2a8ffnameTb4b4b4)
Te6edf3actual_db_hash Tff7b72= Tff7b72selfTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdatabase_pathTb4b4b4)
Tff7b72if Te6edf3actual_db_hash Tff7b72!= Te6edf3manifest_filesTff7b72.Td2a8ffgetTb4b4b4(Te6edf3db_relTb4b4b4)Tb4b4b4:
Te6edf3is_db_okTb4b4b4, Te6edf3db_msg Tff7b72= Tff7b72selfTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdatabase_pathTb4b4b4)
Tff7b72if Tff7b72not Te6edf3is_db_okTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDatabase structural issue: Tffd700{Te6edf3db_msgTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3actual_entropy Tff7b72= Tff7b72selfTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdatabase_pathTb4b4b4)
Te6edf3saved_entropy Tff7b72= Te6edf3manifest_metadataTff7b72.Td2a8ffgetTb4b4b4(Te6edf3db_relTb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)Tff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(
Te6edf3saved_entropy Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Tffa657absTb4b4b4(Te6edf3actual_entropy Tff7b72- Te6edf3saved_entropyTb4b4b4) Tff7b72> T79c0ff1.0
Tb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffDatabase structural anomaly (Entropy Δ: Tffd700{Tffa657absTb4b4b4(Te6edf3actual_entropy Tff7b72- Te6edf3saved_entropyTb4b4b4)Tffd700:Ta5d6ff.2fTffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# Critical identity/config files only during fast startup path.
Tff7b72if Te6edf3critical_onlyTb4b4b4:
Tff7b72for Te6edf3rel_pathTb4b4b4, Te6edf3expected_hash Tff7b72in Te6edf3manifest_filesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_should_ignoreTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Tffa657anyTb4b4b4(Te6edf3marker Tff7b72in Te6edf3rel_path Tff7b72for Te6edf3marker Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffidentityTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3full_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72/ Te6edf3rel_path
Tff7b72if Tff7b72not Te6edf3full_pathTff7b72.Td2a8ffexistsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFile missing: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72continue
Te6edf3actual_hash Tff7b72= Tff7b72selfTff7b72.Td2a8ff_hash_fileTb4b4b4(Te6edf3full_pathTb4b4b4)
Tff7b72if Te6edf3actual_hash Tff7b72!= Te6edf3expected_hashTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffCritical security component integrity compromised: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3issuesTb4b4b4:
Te6edf3m_date Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdateTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4)
Te6edf3m_time Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftimeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4)
Te6edf3issuesTff7b72.Td2a8ffinsertTb4b4b4(
T79c0ff0Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffLast integrity snapshot: Tffd700{Te6edf3m_dateTffd700}Ta5d6ff Tffd700{Te6edf3m_timeTffd700}Ta5d6ff (Identity: Tffd700{Te6edf3m_idTffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffissues Tff7b72= Te6edf3issues
Tff7b72return Tffa657lenTb4b4b4(Te6edf3issuesTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4, Te6edf3issues
T8b949e# Check other critical files in storage_dir
Tff7b72for Te6edf3rootTb4b4b4, Te6edf3_Tb4b4b4, Te6edf3files_in_dir Tff7b72in Te6edf3osTff7b72.Td2a8ffwalkTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3file Tff7b72in Te6edf3files_in_dirTb4b4b4:
Te6edf3full_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3rootTb4b4b4) Tff7b72/ Te6edf3file
Te6edf3rel_path Tff7b72= Tffa657strTb4b4b4(Te6edf3full_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_should_ignoreTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4:
Tff7b72continue
T8b949e# Database handled separately
Tff7b72if Te6edf3full_path Tff7b72== Tff7b72selfTff7b72.Td2a8ffdatabase_pathTb4b4b4:
Tff7b72continue
Te6edf3actual_hash Tff7b72= Tff7b72selfTff7b72.Td2a8ff_hash_fileTb4b4b4(Te6edf3full_pathTb4b4b4)
Tff7b72if Te6edf3rel_path Tff7b72in Te6edf3manifest_filesTb4b4b4:
Tff7b72if Te6edf3actual_hash Tff7b72!= Te6edf3manifest_filesTb4b4b4[Te6edf3rel_pathTb4b4b4]Tb4b4b4:
Te6edf3actual_entropy Tff7b72= Tff7b72selfTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3saved_entropy Tff7b72= Te6edf3manifest_metadataTff7b72.Td2a8ffgetTb4b4b4(Te6edf3rel_pathTb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)Tff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3saved_size Tff7b72= Te6edf3manifest_metadataTff7b72.Td2a8ffgetTb4b4b4(Te6edf3rel_pathTb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)Tff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4,
T79c0ff0Tb4b4b4,
Tb4b4b4)
Te6edf3actual_size Tff7b72= Te6edf3full_pathTff7b72.Td2a8ffstatTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffst_size
Te6edf3is_critical Tff7b72= Tffa657anyTb4b4b4(
Te6edf3c Tff7b72in Te6edf3rel_path Tff7b72for Te6edf3c Tff7b72in Tb4b4b4[Ta5d6ff"Ta5d6ffidentityTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4]
Tb4b4b4)
Tff7b72if Te6edf3is_criticalTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffCritical security component integrity compromised: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72elif Tb4b4b4(
Te6edf3saved_entropy Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Tffa657absTb4b4b4(Te6edf3actual_entropy Tff7b72- Te6edf3saved_entropyTb4b4b4) Tff7b72> T79c0ff1.5
Tb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffNon-linear content shift detected in Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff (Entropy Δ: Tffd700{Tffa657absTb4b4b4(Te6edf3actual_entropy Tff7b72- Te6edf3saved_entropyTb4b4b4)Tffd700:Ta5d6ff.2fTffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72elif Te6edf3saved_size Tff7b72and Te6edf3actual_size Tff7b72!= Te6edf3saved_sizeTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffFile size divergence: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff (Tffd700{Te6edf3saved_sizeTffd700}Ta5d6ff -> Tffd700{Te6edf3actual_sizeTffd700}Ta5d6ff bytes)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFile signature mismatch: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffNew file detected: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# Check for missing files
Tff7b72for Te6edf3rel_path Tff7b72in Te6edf3manifest_filesTb4b4b4:
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_should_ignoreTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3full_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72/ Te6edf3rel_path
Tff7b72if Tff7b72not Te6edf3full_pathTff7b72.Td2a8ffexistsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFile missing: Tffd700{Te6edf3rel_pathTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3issuesTb4b4b4:
Te6edf3m_date Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdateTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4)
Te6edf3m_time Tff7b72= Te6edf3manifestTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftimeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffUnknownTa5d6ff"Tb4b4b4)
Te6edf3issuesTff7b72.Td2a8ffinsertTb4b4b4(
T79c0ff0Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffLast integrity snapshot: Tffd700{Te6edf3m_dateTffd700}Ta5d6ff Tffd700{Te6edf3m_timeTffd700}Ta5d6ff (Identity: Tffd700{Te6edf3m_idTffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffissues Tff7b72= Te6edf3issues
Tff7b72return Tffa657lenTb4b4b4(Te6edf3issuesTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4, Te6edf3issues
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Tff7b72FalseTb4b4b4, Tb4b4b4[Ta5d6fffTa5d6ff"Ta5d6ffIntegrity check failed: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4]
Tff7b72def Td2a8ffsave_manifestTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Snapshot the current state with extended mathematical metadata."""
Tff7b72tryTb4b4b4:
Te6edf3files Tff7b72= Tb4b4b4{Tb4b4b4}
Te6edf3metadata Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72for Te6edf3rootTb4b4b4, Te6edf3_Tb4b4b4, Te6edf3files_in_dir Tff7b72in Te6edf3osTff7b72.Td2a8ffwalkTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3file Tff7b72in Te6edf3files_in_dirTb4b4b4:
Te6edf3full_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3rootTb4b4b4) Tff7b72/ Te6edf3file
Te6edf3rel_path Tff7b72= Tffa657strTb4b4b4(Te6edf3full_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_should_ignoreTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3filesTb4b4b4[Te6edf3rel_pathTb4b4b4] Tff7b72= Tff7b72selfTff7b72.Td2a8ff_hash_fileTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3metadataTb4b4b4[Te6edf3rel_pathTb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3full_pathTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3full_pathTff7b72.Td2a8ffstatTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffst_sizeTb4b4b4,
Tb4b4b4}
Te6edf3now Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)
Te6edf3manifest Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: T79c0ff2Tb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: Te6edf3nowTff7b72.Td2a8fftimestampTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffdateTa5d6ff"Tb4b4b4: Te6edf3nowTff7b72.Td2a8ffstrftimeTb4b4b4(Ta5d6ff"Ta5d6ff%Ta5d6ffY-Ta5d6ff%Ta5d6ffm-Tffd700%dTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fftimeTa5d6ff"Tb4b4b4: Te6edf3nowTff7b72.Td2a8ffstrftimeTb4b4b4(Ta5d6ff"Ta5d6ff%Ta5d6ffH:Ta5d6ff%Ta5d6ffM:Ta5d6ff%Ta5d6ffSTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffidentityTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ffidentity_hashTb4b4b4,
Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4: Te6edf3filesTb4b4b4,
Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4: Te6edf3metadataTb4b4b4,
Tb4b4b4}
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4, Te6edf3indentTff7b72=T79c0ff2Tb4b4b4)
Tff7b72return Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to save integrity manifest: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────